package com.jxb.netty.network.byteBuffer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;

/**
 * 自我总结：NIO是基于缓存的，提供了ByteBuffer来缓存数据
 *  正常的ByteBuffer读写是通过put和get方法去实现的，但是需要进行读写切换，通过flip方法
 *  对于channel的话，读取channel的数据先需要放到ByteBuffer中，通过channl的read方法，往channel中写的话，通过write方法
 *  ByteBuffer，需要多次写入的话，需要在每次写入之前清空缓存中的数据，通过compact：清空已读的数据，clear：清空所有缓存数据
 *  ByteBuffer的remaining方法，是获取position和limit之间的长度
 *  还可以通过mark标记一个position，然后通过reset进行恢复
 *  绝对读写是指定index写入数据或者读取数据，不改变position的值    (指定位置写入数据或者读取数据)
 */
public class NioTest {
    public static void main(String[] args) throws Exception {
        absoluteRW();
    }

    /**
     * 将channel中的数据写入到ByteBuffer中，再从ByteBuffer中获取数据
     *
     * 1：申请内存空间（堆内存 || 直接内存），ByteBuffer.allocate(),ByteBuffer.allocateDirect()
     * 2：从channel读取数据到ByteBuffer，channel.read(buffer)
     * 3：从ByteBuffer写模式转成读模式，buffer.flip()
     * 4：从Buffer中读取数据，buffer.get()
     * @param key
     * @throws IOException
     */
    private void readChannelByByteBuffer(SelectionKey key) throws IOException {
        SocketChannel sc = (SocketChannel) key.channel();
        //申请一个ByteBuffer堆内存
        ByteBuffer allocateBuf = ByteBuffer.allocate(1024);
        //申请直接内存
        ByteBuffer direct = ByteBuffer.allocateDirect(1024);
        int read = sc.read(allocateBuf);
        //再从Buffer中读取数据
        if (read > 0) {
            //将Buffer的写模式转成读模式，将position设置为0，将limit设置为之前的position
            allocateBuf.flip();
            //从Buffer缓冲区中读取数据
            //remaining()方法：返回position到limit之间的距离
            byte[] bytes = new byte[allocateBuf.remaining()];
            allocateBuf.get(bytes);
        }
    }

    /**
     * 1：申请ByteBuffer内存
     * 2：将字节数据添加到BufferByte中，byteBuffer.put(bytes)
     * 3：将ByteBuffer的写模式转成读模式，byteBuffer.flip()
     * 4：将ByteBuffer中的数据写入到channel，channel.write(bytebuffer)
     * @param key
     * @throws IOException
     */
    private void writeChannelByByteBuffer(SelectionKey key) throws IOException {
        //定义需要写入的字节数组
        String response = "写入数据";
        byte[] bytes = response.getBytes();
        //申请ByteBuffer内存
        ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
        //将byte数据添加到byteBuffer中
        byteBuffer.put(bytes);
        //将ByteBuffer的写模式转成读模式
        byteBuffer.flip();
        //将ByteBuffer中的数据写入到channel
        SocketChannel sc = (SocketChannel) key.channel();
        sc.write(byteBuffer);
        //读取完，清空缓冲区
        byteBuffer.clear();//清空整个缓冲区
        byteBuffer.compact();//清空已经读过的数据
    }

    /**
     * 正常的读写模式
     * 1：向buffer中写入数据，bufferBuffer.put
     * 2：将byteBuffer从写模式转为读模式，byteBuffer.flip
     * 3：读取buffer中的数据，buffer.get(bytes)
     * 4：清空缓冲区，buffer.compact()
     */
    private static void writeAndRead() {
        //定义需要写入的字节数组
        String response = "写入数据";
        byte[] bytes = response.getBytes();
        //申请ByteBuffer内存
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //将byte数据添加到byteBuffer中
        byteBuffer.put(bytes);
        //将写模式转成读模式
        byteBuffer.flip();
        byte[] bytes1 = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes1);
        String s = new String(bytes1, StandardCharsets.UTF_8);
        System.out.println("buffer中的数据："+s);

        //将position设置为0，重新读取
        byteBuffer.rewind();
        byte[] bytes2 = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes2);
        String s2 = new String(bytes2, StandardCharsets.UTF_8);
        System.out.println("重新读取buffer中的数据："+s2);
        bytes2.clone();

        //再次写入

        //将读模式转成写模式
        byteBuffer.flip();
        //清空缓冲区：清空缓冲区操作之前，需要将读模式转成写模式
        byteBuffer.compact();
        String response1 = "再次写入数据";
        byte[] bytes3 = response1.getBytes();
        byteBuffer.put(bytes3);
        byte[] bytes4 = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes4);
        String s3 = new String(bytes4, StandardCharsets.UTF_8);
        System.out.println("再次写入数据，并读取："+s3);
        bytes2.clone();

    }

    /**
     * 绝对读写
     */
    private static void absoluteRW() throws UnsupportedEncodingException {
        //定义需要写入的字节数组
        byte b = 1;
        //申请ByteBuffer内存
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        //绝对写入：写入时指定index
        byteBuffer.put(1,b);
        //将写模式转成读模式
        byteBuffer.flip();
        //绝对读取：读取时指定index
        byte[] bytes1 = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes1);
        String s = new String(bytes1, StandardCharsets.UTF_8);
        System.out.println("index:1--------------------------->"+s);
        byte[] bytes2 = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes2);
        String s2 = new String(bytes2, StandardCharsets.UTF_8);
        System.out.println("index:2--------------------------->"+s2);
    }

}
